package com.inspur.edp.common.type.parser;

import com.inspur.edp.common.type.Method;
import com.inspur.edp.common.type.Parameter;
import com.inspur.edp.common.type.Type;
import com.inspur.edp.common.type.TypeRefer;
import com.inspur.edp.common.type.exception.TypeException;
import com.inspur.edp.common.type.cache.TypeThreadCache;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lizhaorui
 * @date 2025/8/18
 * @description
 */
public class TypeParser {

    public static Method getMethod(TypeRefer typeRefer, String methodCode, int providerPCount) {
        Type dslType = TypeThreadCache.getType(typeRefer);
        for (Method methodMd : dslType.getMethods()) {
            if (methodMd.getCode().equalsIgnoreCase(methodCode)) {
                if (methodMd.getParameters() != null) {
                    int totalCount = methodMd.getParameters().size();
                    boolean hasVarArgs = false;
                    if (totalCount > 0) {
                        Parameter lastParameter = methodMd.getParameters().get(totalCount - 1);
                        hasVarArgs = lastParameter.isVarArgs();
                    }
                    if (hasVarArgs) {
                        if (providerPCount >= methodMd.getParameters().size()) {
                            return methodMd;
                        }
                    } else {
                        if (providerPCount == methodMd.getParameters().size()) {
                            return methodMd;
                        }
                    }
                } else {
                    if (providerPCount == 0) {
                        return methodMd;
                    }
                }
            }
        }
        throw new TypeException(String.format("type source %s typeId %s not exists method %s", dslType.getSource(), dslType.getTypeId(), methodCode));
    }

    public static Method getMethod(TypeRefer typeRefer, String methodCode, List<TypeRefer> parameterTypes) {
        Type dslType = TypeThreadCache.getType(typeRefer);
        String[] notMatchInfo = null;
        for (Method methodMd : dslType.getMethods()) {
            if (methodMd.getCode().equalsIgnoreCase(methodCode)) {
                if (parameterTypes == null || parameterTypes.size() == 0) {
                    if (methodMd.getParameters() == null || methodMd.getParameters().size() == 0) {
                        return methodMd;
                    } else {
                        continue;
                    }
                } else {
                    if (methodMd.getParameters() == null || methodMd.getParameters().size() == 0) {
                        continue;
                    } else {
                        int parameterCount = methodMd.getParameters().size();
                        Parameter lastParameter = methodMd.getParameters().get(parameterCount - 1);
                        boolean hasVarArgs = lastParameter.isVarArgs();
                        if (!hasVarArgs) {
                            if (parameterTypes.size() != parameterCount) {
                                continue;
                            } else {
                                int compareIndex = 0;
                                boolean match = true;
                                for (TypeRefer providerType : parameterTypes) {
                                    TypeRefer requiredType = methodMd.getParameters().get(compareIndex).getType();
                                    if (dslType.getGenericTypeNames() != null) {
                                        int index = dslType.getGenericTypeNames().indexOf(requiredType.getTypeId());
                                        if (index >= 0) {
                                            requiredType = typeRefer.getGenericTypes().get(index);
                                        }
                                    }
                                    boolean matchResult = TypeCompareMgr.equalOrIsInstance(providerType, requiredType);
                                    if (!matchResult) {
                                        // 特殊类型处理
                                        match = false;
                                        notMatchInfo = new String[2];
                                        notMatchInfo[0] = requiredType.getSource() + " :: " + requiredType.getTypeId();
                                        notMatchInfo[1] = providerType.getSource() + " :: " + providerType.getTypeId();
                                        break;
                                    }
                                    compareIndex++;
                                }
                                if (match) {
                                    return methodMd;
                                }
                            }
                        } else {
                            if (parameterTypes.size() < parameterCount) {
                                continue;
                            }
                            int compareIndex = 0;
                            boolean match = true;
                            for (TypeRefer providerType : parameterTypes) {
                                TypeRefer requiredType = null;
                                if (compareIndex >= parameterCount - 1) {
                                    requiredType = methodMd.getParameters().get(parameterCount - 1).getType();
                                } else {
                                    requiredType = methodMd.getParameters().get(compareIndex).getType();
                                }
                                if (dslType.getGenericTypeNames() != null) {
                                    int index = dslType.getGenericTypeNames().indexOf(requiredType.getTypeId());
                                    if (index >= 0) {
                                        requiredType = typeRefer.getGenericTypes().get(index);
                                    }
                                }
                                boolean matchResult = TypeCompareMgr.equalOrIsInstance(providerType, requiredType);
                                if (!matchResult) {
                                    // 特殊类型处理
                                    match = false;
                                    notMatchInfo = new String[2];
                                    notMatchInfo[0] = requiredType.getSource() + " :: " + requiredType.getTypeId();
                                    notMatchInfo[1] = providerType.getSource() + " :: " + providerType.getTypeId();
                                    break;
                                }
                                compareIndex++;
                            }
                            if (match) {
                                return methodMd;
                            }
                        }
                    }
                }
            }
        }
        if (dslType.getMethod(methodCode) != null) {
            if (notMatchInfo != null) {
                throw new TypeException(String.format("type source %s typeId %s", dslType.getSource(), dslType.getTypeId()) + ",method:" + methodCode + ",parameter require is " + notMatchInfo[0] + ",provider is " + notMatchInfo[1]);
            } else {
                throw new TypeException(String.format("type source %s typeId %s", dslType.getSource(), dslType.getTypeId()) + ",method:" + methodCode + ",parameter count not match ");
            }
        } else {
            throw new TypeException(String.format("type source %s typeId %s", dslType.getSource(), dslType.getTypeId()) + " do not contain method " + methodCode);
        }
    }

    public static TypeRefer getFieldType(TypeRefer typeRefer, String fieldCode) {
        Type dslType = TypeThreadCache.getType(typeRefer);
        Map<String, TypeRefer> genericTypeMap = getTypeReferMap(typeRefer, dslType);
        TypeRefer refer = dslType.getField(fieldCode).getType();
        return refer.tryReplaceGenericType(genericTypeMap);
    }

    public static TypeRefer getMethodRetType(TypeRefer typeRefer, String methodCode, List<TypeRefer> parameterTypes) {
        Method method = getMethod(typeRefer, methodCode, parameterTypes);
        TypeRefer returnType = method.getReturnType();
        Type dslType = TypeThreadCache.getType(typeRefer);
        Map<String, TypeRefer> genericTypeMap = getTypeReferMap(typeRefer, dslType);
        return returnType.tryReplaceGenericType(genericTypeMap);
    }

    public static TypeRefer getMethodRetType(TypeRefer typeRefer, String methodCode, int parameterCount) {
        Method method = getMethod(typeRefer, methodCode, parameterCount);
        TypeRefer returnType = method.getReturnType();
        Type dslType = TypeThreadCache.getType(typeRefer);
        Map<String, TypeRefer> genericTypeMap = getTypeReferMap(typeRefer, dslType);
        return returnType.tryReplaceGenericType(genericTypeMap);
    }

    private static Map<String, TypeRefer> getTypeReferMap(TypeRefer typeRefer, Type dslType) {
        Map<String, TypeRefer> genericTypeMap = null;
        List<String> genericNames = dslType.getGenericTypeNames();
        if (genericNames != null && genericNames.size() > 0) {
            genericTypeMap = new HashMap<>();
            int index = 0;
            for (String name : genericNames) {
                genericTypeMap.put(name, typeRefer.getGenericTypes().get(index));
                index++;
            }
        }
        return genericTypeMap;
    }

}